package com.spa.domain.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.spa.application.command.common.rule.KpiAddCommand;
import com.spa.application.command.common.rule.RuleAddCommand;
import com.spa.application.dto.common.rule.KpiDto;
import com.spa.domain.entity.OrderInfo;
import com.spa.domain.entity.Rule;
import com.spa.infrastructure.enums.dict.rule.RuleClassifyEnum;
import com.spa.infrastructure.enums.dict.rule.RuleFlagEnum;
import com.spa.infrastructure.enums.dict.rule.RuleKpiConditionEnum;
import com.spa.infrastructure.enums.dict.rule.RuleKpiEnum;
import com.spa.infrastructure.enums.dict.rule.RuleTypeEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.mapper.RuleMapperStruct;
import com.spa.infrastructure.repository.condition.RuleCondition;
import com.spa.infrastructure.repository.po.RulePO;
import com.spa.infrastructure.repository.service.RuleRepositoryService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.jsf.FacesContextUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 规则  domain
 *
 * @author: lig
 * @date: 2024-03-26
 */
@Service
@Slf4j
public class RuleService {

    @Resource
    private RuleRepositoryService ruleRepositoryService;
    @Resource
    private MassagistService massagistService;
    @Resource
    private ShopService shopService;

    /**
     * 添加规则
     *
     * @author: lig
     * @date: 2024-03-26
     */
    public void addRule(List<RuleAddCommand> ruleParamList, Long relevanceId, RuleClassifyEnum ruleClassifyEnum) {

        delRule(relevanceId, ruleClassifyEnum);
        ruleParamList.stream().forEach(a -> {
            RuleTypeEnum rte = RuleTypeEnum.byCode(a.getType());
            if(ObjectUtils.isEmpty(rte)){
                throw new SpaException("规则类型错误");
            }
            RuleFlagEnum rfe = RuleFlagEnum.byCode(a.getFlag());
            if(ObjectUtils.isEmpty(rfe)){
                throw new SpaException("规则标识错误");
            }
            RulePO po = new RulePO();
            po.setRelevanceId(relevanceId);
            po.setSerial(a.getSerial());
            po.setType(rte.getCode());
            po.setFlag(rfe.getCode());
            po.setClassify(ruleClassifyEnum.getCode());
            po.setVal(a.getVal());

            List<KpiAddCommand> kpiList = a.getKpiList();
            if (CollUtil.isNotEmpty(kpiList)) {
                po.setExpression(JSONUtil.toJsonStr(kpiList));
            }
            ruleRepositoryService.addInfo(po);
        });

    }


    /**
     * 删除规则
     *
     * @param relevanceId 相关ID
     * @author: lig
     * @date: 2024-03-27
     */
    public void delRule(Long relevanceId, RuleClassifyEnum ruleClassifyEnum) {
        RuleCondition ruleCondition = new RuleCondition();
        ruleCondition.setRelevanceId(relevanceId);
        ruleCondition.setClassify(ruleClassifyEnum.getCode());
        List<RulePO> rulePOList = ruleRepositoryService.list(ruleCondition);
        if (CollUtil.isNotEmpty(rulePOList)) {
            ruleRepositoryService.removeBatchByIds(rulePOList.stream().map(RulePO::getId).toList());
        }

    }


    /**
     * 获取规则
     *
     * @author: lig
     * @date: 2024-03-27
     */
    public List<Rule> listRule(RuleCondition ruleCondition) {
        List<RulePO> rulePOList = ruleRepositoryService.list(ruleCondition);
        return RuleMapperStruct.INSTANCE.po2Entity4List(rulePOList);

    }

    /**
     * @description: 验证 技师规则参数
     * @author: lig
     * @date: 2024/3/27
     */
    public void verifyMassagistRuleParam(List<RuleAddCommand> ruleAddCommandList, Long shopId) {

        RuleCondition ruleCondition = new RuleCondition();
        ruleCondition.setRelevanceId(shopId);
        //获取门店规则
        List<Rule> shopRuleList = listRule(ruleCondition);
        if (CollUtil.isEmpty(shopRuleList)) {
            throw new SpaException("门店规则不存在");
        }

        ruleAddCommandList.stream().forEach(m -> {
            RuleFlagEnum ruleFlagEnum = RuleFlagEnum.byCode(m.getFlag());
            //技师订单
            if (RuleFlagEnum.$10.equals(ruleFlagEnum)) {
                //门店订单 规则
                List<Rule> shopOrderImmobilityRuleList = shopRuleList.stream().filter(s1 -> RuleFlagEnum.$10.getCode().equals(s1.getFlag())).toList();

                //订单  固定规则
                if (RuleTypeEnum.$10.getCode().equals(m.getType())) {
                    //门店最大规则
                    Rule shopRule = shopOrderImmobilityRuleList.stream().max(Comparator.comparing(Rule::getVal)).get();
                    Double shopRuleVal = Convert.toDouble(shopRule.getVal(), 0d);
                    Double massagistRuleVal = Convert.toDouble(m.getVal(), 0d);
                    if (massagistRuleVal > shopRuleVal) {
                        throw new SpaException(String.format("技师的订单固定比例不能超过门店，技师：%s，门店：%s", massagistRuleVal, shopRuleVal));
                    }
                }
                if (RuleTypeEnum.$20.getCode().equals(m.getType())) {
                    verifyFloatRule(shopOrderImmobilityRuleList, m, ruleFlagEnum);
                }
            }
            //技师 加钟订单
            if (RuleFlagEnum.$20.equals(ruleFlagEnum)) {
                //门店订单加钟规则
                List<Rule> shopOrderExtRuleList = shopRuleList.stream().filter(s1 -> RuleTypeEnum.$20.getCode().equals(s1.getType())).toList();
                verifyFloatRule(shopOrderExtRuleList, m, ruleFlagEnum);

            }

        });

    }


    /**
     * @description: 添加技师规则
     * @author: lig
     * @date: 2024/3/27
     */
    public void addMassagistRule(List<RuleAddCommand> ruleAddCommandList, Long shopId, Long massagistId) {
        //规则
        if (CollUtil.isNotEmpty(ruleAddCommandList)) {
            verifyMassagistRuleParam(ruleAddCommandList, shopId);
            addRule(ruleAddCommandList, massagistId, RuleClassifyEnum.$10);
        }

    }

    /**
     * @param condition  比较条件
     * @param bigDecimal 比较参数1
     * @param kpiVal     指标设置的阈值
     * @description: 校验指标
     * @author: lig
     * @date: 2024/3/28
     */
    public boolean verifyKpi(RuleKpiConditionEnum condition, BigDecimal bigDecimal, BigDecimal kpiVal) {
        if (RuleKpiConditionEnum.$gt.equals(condition)) {
            if (bigDecimal.doubleValue() > kpiVal.doubleValue()) {
                return true;
            }
        }
        return false;
    }

    /**
     * @description: 获取门店比例
     * @author: lig
     * @date: 2024/3/28
     */
    public Double getRatio4Shop(List<Rule> ruleList, OrderInfo orderInfo) {

        //获取门店业绩
        Integer perf = shopService.getShopPerf(orderInfo);
        //获取门店加钟率
        Double extraRatio = shopService.getShopExtraRatio(orderInfo.getShopId());
        //获取门店好评率
        Double positiveRatio = shopService.getShopPositiveRatio(orderInfo.getShopId());

        Rule rule = getRule(ruleList, perf, extraRatio, positiveRatio);
        log.debug(String.format("门店规则命中，订单号：%s，业绩：%s，加钟率：%s，好评率：%s，规则%s比例：%s",
                orderInfo, perf, extraRatio, positiveRatio, rule.getSerial(), rule.getVal()));

        return Convert.toDouble(rule.getVal(), 0d);
    }

    /**
     * @description: 获取技师比例
     * @author: lig
     * @date: 2024/3/28
     */
    public Double getRatio4Massagist(List<Rule> ruleList, OrderInfo orderInfo) {

        //获取技师业绩
        Integer perf = massagistService.getMassagistPerf(orderInfo);
        //获取技师加钟率
        Double extraRatio = massagistService.getMassagistExtraRatio(orderInfo.getMassagistId());
        //获取技师好评率
        Double positiveRatio = massagistService.getMassagistPositiveRatio(orderInfo.getMassagistId());

        Rule rule = getRule(ruleList, perf, extraRatio, positiveRatio);
        log.debug(String.format("技师规则命中，订单号：%s，业绩：%s，加钟率：%s，好评率：%s，规则%s比例：%s",
                orderInfo, perf, extraRatio, positiveRatio, rule.getSerial(), rule.getVal()));

        return Convert.toDouble(rule.getVal(), 0d);
    }

    private Rule getRule(List<Rule> ruleList, Integer perf, Double extraRatio, Double positiveRatio) {
        Rule rule = ruleList.get(0);
        for (int i = 0; i < ruleList.size(); i++) {
            Rule a = ruleList.get(i);
            List<KpiDto> kpiDtoList = Rule.expressionToDtoList(a.getExpression());
            if (CollUtil.isNotEmpty(kpiDtoList)) {
                //技师指标
                boolean b = verifyKpi(kpiDtoList, perf, extraRatio, positiveRatio);
                if (b) {
                    rule = a;
                } else {
                    break;
                }
            }
        }
        return rule;
    }

    /**
     * @description: 校验浮动规则
     * @author: lig
     * @date: 2024/3/30
     */
    private void verifyFloatRule(List<Rule> ruleList, RuleAddCommand addCommand, RuleFlagEnum ruleFlagEnum) {
        //门店订单 浮动规则
        List<Rule> shopOrderFloatRuleList = ruleList.stream().filter(s1 -> ruleFlagEnum.getCode().equals(s1.getFlag()) && RuleTypeEnum.$20.getCode().equals(s1.getType())).sorted(Comparator.comparing(Rule::getSerial)).toList();

        if (CollUtil.isEmpty(shopOrderFloatRuleList)) {
            return;
        }
        double compare1 = Convert.toDouble(shopOrderFloatRuleList.get(shopOrderFloatRuleList.size() - 1).getVal(), 0d);
        double compare2 = Convert.toDouble(addCommand.getVal(), 0d);
        int index = addCommand.getSerial() - 1;
        if (index < shopOrderFloatRuleList.size()) {
            Rule rule = shopOrderFloatRuleList.get(index);
            compare1 = Convert.toDouble(rule.getVal(), 0d);
        }
        if (compare2 > compare1) {
            String s = "技师的浮动比例不能超过对应门店的浮动比例，技师：%s，门店：%s，序号：%s，分类：%s";
            throw new SpaException(String.format(s, compare2, compare1, index + 1, ruleFlagEnum.getDesc()));
        }

    }


    /**
     * @description: 校验指标
     * @author: lig
     * @date: 2024/4/2
     */
    public Boolean verifyKpi(List<KpiDto> kpiDtoList, Integer perf, Double extraRatio, Double positiveRatio) {
        AtomicBoolean boo = new AtomicBoolean(true);
        kpiDtoList.stream().forEach(k1 -> {
            if (!boo.get()) {
                return;
            }

            BigDecimal kpiVal = new BigDecimal(k1.getKpiVal());
            BigDecimal bigDecimal = BigDecimal.ZERO;

            //业绩====
            if (RuleKpiEnum.$10.getDesc().equals(k1.getKpiKey())) {
                bigDecimal = BigDecimal.valueOf(perf);
            }
            //加钟率====
            if (RuleKpiEnum.$20.getDesc().equals(k1.getKpiKey())) {
                kpiVal = kpiVal.multiply(BigDecimal.valueOf(100));
                bigDecimal = BigDecimal.valueOf(extraRatio * 100);
            }
            //好评率====
            if (RuleKpiEnum.$30.getDesc().equals(k1.getKpiKey())) {
                kpiVal = kpiVal.multiply(BigDecimal.valueOf(100));
                bigDecimal = BigDecimal.valueOf(positiveRatio * 100);
            }

            if (!verifyKpi(RuleKpiConditionEnum.byCode(k1.getCondition()), bigDecimal, kpiVal)) {
                boo.set(false);
            }

        });

        return boo.get();

    }


    /**
     * 基本 指标信息
     *
     * @author: lig
     * @date: 2024-03-29
     */
    @Data
    public class KepInfo {


        /**
         * 业绩
         */
        private Integer perf;

        /**
         * 加钟率
         */
        private Double extraRatio;

        /**
         * 好评率
         */
        private Double positiveRatio;


    }


}
